perm filename GRNSAI.SAI[S,HE]6 blob sn#721895 filedate 1983-08-05 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00024 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00004 00002	These routines are for outputting gray-level pictures on the Grinnell.
C00010 00003	INTERNAL PROCEDURE SNOOZE(INTEGER TIME(0))
C00013 00004	PROCEDURE PUPERROR(INTEGER ErrorCode String ErrMess)
C00015 00005	PROCEDURE START11
C00016 00006	PROCEDURE ENETSTART
C00019 00007	INTERNAL PROCEDURE BUFOUT
C00023 00008	PROCEDURE ENETOUT(INTEGER ARRAY INSTRUCTIONS INTEGER INSTLEFT)
C00025 00009	INTERNAL INTEGER PROCEDURE ENETWORDIN
C00026 00010	INTERNAL INTEGER PROCEDURE GWORDIN
C00028 00011	INTERNAL PROCEDURE GRNINI
C00033 00012	INTERNAL SIMPLE PROCEDURE DEMOMODE(STRING DEVICE,FILENAME)
C00035 00013	INTERNAL PROCEDURE GRNFIN
C00036 00014	PROCEDURE DEMOWORD(INTEGER WORD)
C00037 00015	INTERNAL SIMPLE PROCEDURE GRNINS(INTEGER WORD)
C00039 00016	INTERNAL PROCEDURE ENETIN(INTEGER ARRAY_ADDR, WORDCOUNT, READBACKMODE(0))
C00040 00017	INTERNAL PROCEDURE GRNIN(INTEGER ADDR, COUNT, READBACKMODE(0), USETBITS(0))
C00045 00018	INTERNAL SIMPLE PROCEDURE EXE11BUF(INTEGER ADDR, BUFLEN)
C00051 00019	INTERNAL PROCEDURE TSTGRN(INTEGER TESTNO)
C00052 00020	INTERNAL PROCEDURE MAPGRN(INTEGER CARD,TABL,NBITS)
C00053 00021	INTERNAL PROCEDURE ERASEG(INTEGER CHAN,BITS('7777))
C00054 00022	INTERNAL PROCEDURE VIDGRN(INTEGER LEFT, TOP, CHAN INTEGER ARRAY PIC
C00059 00023	! routines to input a picture from the grnnell
C00065 00024	END "GRNSAI"
C00066 ENDMK
C⊗;
Comment These routines are for outputting gray-level pictures on the Grinnell.
  They are a temporary hack to "hold the dike" until the device-independent
  graphics library can be completed and should be regarded as such. AAM '80;
Comment Hah!  Such youthful optimism.  AAM '82;
Comment Temporary my ass.  Added Ethernet bullshit.  AAM,CR '83;

ENTRY VIDGRN,TSTGRN,MAPGRN,GRNINI,GRNFIN,GRNINS,BUFOUT, SNOOZE, GRNIN, GWORDIN,
	ENETWORDIN,INPIC, ENETIN,
        EXE11BUF, QUICKGIN, QUICKBUF;
BEGIN "GRNSAI"

Comment misc. definitions;
REQUIRE "{}<>" DELIMITERS;

REQUIRE "GRNFHD.SAI[HDR,HE]" SOURCE_FILE;
REQUIRE "PIXHDR.SAI[HDR,HE]" SOURCE_FILE;
REQUIRE "GRNDEF[HDR,HE]" SOURCE_FILE;
REQUIRE "ELFHDR.SAI[HDR,HE]" SOURCE_FILE;
REQUIRE "CRDHDR.SAI[HDR,HE]" SOURCE_FILE;
REQUIRE "PUPFAI[S,HE]" LOAD_MODULE;
REQUIRE "PUPFAI.ENV[HDR,HE]" SOURCE_FILE;
REQUIRE "PUPSAI[S,HE]" LOAD_MODULE;
REQUIRE "PUPSAI.ENV[HDR,HE]" SOURCE_FILE;

DEFINE TRACE={FALSE};	COMMENT Print out debugging marks;
DEFINE SEEINSTR={FALSE}; COMMENT Look at Grinnell instructions in octal before executring them;
DEFINE HANGNOISY={TRUE}; COMMENT Complain loudly when DR11 hangs;
DEFINE HANGWAIT={FALSE}; COMMENT Allow user to examine signals before resetting
				PDP11 when DR11 hangs;
DEFINE HANGRETRY={FALSE}; COMMENT When DR11 hangs, keep retrying until it doesn't;
DEFINE SPEEDCHECK={FALSE}; COMMENT Special timing hacks;
DEFINE GRNDEV={TRUE};	COMMENT Grinnell a device yet?;
DEFINE BUSTCHK={FALSE};  COMMENT Check BUSTED.GRN for latest status?;
DEFINE VAXCHK={TRUE};  COMMENT Check ONVAX.GRN to see if Grinnell is on the vax;

DEFINE ! = {COMMENT};
DEFINE CRLF = { ('15&'12) };

DEFINE GRNBUF(BUFFER,BUFLEN,BITS) = {
	Comment Cause DR-11B to do its thing;
IF NOT BUSTED THEN
 BEGIN
	WAITFOR11;
	IFC TRACE THENC print("!"); ENDC
	ELFBKO(START11MEM,(BUFLEN),BUFFER,(BITS)); Comment move the buffer to the 11;
	ELFOUT(DRWC,-(BUFLEN));	Comment NOTE: word count not byte count;
	ELFOUT(DRBA,(START11MEM LAND '177777) LSH 1); comment MAKE INTO BYTE ADR;
	ELFOUT(DRST,DRWRITE);	     comment Grinnell needs at least one clock;
	ELFOUT(DRST,GO LOR DRWRITE LOR XBA);     comment cycle between setting of;
						 comment write and go;
 END;
}; ! end of GRNBUF;

Comment global variables;
BOOLEAN DEMO;			COMMENT Whether a demo file is being created;
INTEGER DEMOCHAN;		COMMENT The channel the file is open on;
INTEGER DEMOSAVE;		COMMENT Word used to pack instructions;
BOOLEAN EVENWORD;
INTEGER DEMBREAK,DEMEOF;	COMMENT For demo file stuff;
BOOLEAN BUSTED;			Comment Whether or not Grinnell is in normal mode;
INTEGER NXTLOC;			Comment next PDP11 address to load;
SAFE Comment to speed up GRNINS;
INTEGER ARRAY INSBUF[1:LEN11MEM]; Comment 10-buffer for 11 memory.;
INTEGER BUFSIZE;		Comment No. of 16-bit words that go in INSBUF;
INTEGER LOKCHAN;	Comment channel used for opening file which locks grinnell;

BOOLEAN ONVAX;		! Set to true if the grinnell is on the Whitney;
INTEGER PUPCHAN;	! Channel for ethernet communication to Whitney;
INTEGER PUP36SIZE,PUP16SIZE;	! Size of PUP buffer in system;
BOOLEAN CONNECTED;	! Set to true if a connection has been established;
INTERNAL PROCEDURE SNOOZE(INTEGER TIME(0));
Comment SNOOZE is the delay that is used for waiting for the DR11-B;
Comment sleeps for 1/60 of a second (until next tick, I guess);
Comment unless argument is nonzero, then it sleeps that many seconds;

BEGIN "SNOOZE"
   QUICK_CODE "SLEEP"
      MOVE '13,TIME;
      CALLI '13,'31
   END "SLEEP";
IFC TRACE THENC   Print(":");  ENDC
END "SNOOZE";  Comment what a kludge;

INTERNAL PROCEDURE WAITFOR11;
BEGIN "WAITFOR11"
INTEGER SLEEPY;
    Comment wait until the DR11B is ready and the arm is not moving;
	SLEEPY ← 0;
	WHILE ¬(ELFIN(DRST) LAND READY) DO 
	    BEGIN SNOOZE; SLEEPY ← SLEEPY+1;
IFC TRACE THENC
!		  IF SLEEPY > 20 THEN ;
		    PRINT("DRST: ",CVOS(ELFIN(DRST)),'15&'12);
ENDC
	    COMMENT Added code to check for hanging not due to Grinnell;
	    IF SLEEPY > 120 COMMENT Allow 2 sec for this one;
		THEN BEGIN "HUNG BUT NOT OUR FAULT"
		STRING RESET;
		PRINT("The PDP-11 appears to be hung.  Shall I try a RESET?");
		PRINT('15&'12&"Don't say yes unless you are prepared to deal with");
		PRINT('15&'12&"an angry arm hacker (check first):  ");
		RESET←INCHWL;
		IF (RESET="Y") OR (RESET="y") THEN POWERFAIL ELSE
		    BEGIN "HARLYNS HACK"
			COMMENT This works sometimes;
			PRINT("Trying recovery by zeroing wordcount."&'15&'12);
			ELFOUT(DRWC,0);
			ELFOUT(DRST,0);
		    END "HARLYNS HACK";
		SLEEPY←0;
		END "HUNG BUT NOT OUR FAULT";
	    END;
END "WAITFOR11";

PROCEDURE PUPERROR(INTEGER ErrorCode; String ErrMess);
Comment This routine checks outputs a message according to the error code;
BEGIN "PUPERROR"
    IF ErrorCode ≠ 0 
	Then Begin
	    Print(ErrMess,crlf);
	    Case ErrorCode OF Begin
		[1] Print("Illegal channel number");
		[2] Print("OPEN failed on device PUP");
		[3] Print("MTAPE to establish connection failed");
		[4] Print("OUT failed while outputting buffer");
		[5] Print("IN failed while reading buffer");
		[6] Print("odd number of bytes in packet on input");
		[7] Print("block-mode output buffer too long");
		ELSE Print("Unknown Error: ",ErrorCode)
	    End;
	    Print(crlf);
	End;
END "PUPERROR";

PROCEDURE START11;
Comment This routine initializes the connection for the Grinnell to the
	PDP-11.;
BEGIN "START11"
    ELFINI;
    BUFSIZE←LEN11MEM;	Comment one pdp-11 word per Grn instruction;
END;
PROCEDURE ENETSTART;
Comment This routine initializes an ethernet connection to the VAX for
	sending instructions to the grinnell.
        Sets CONNECTED to True if a connection is established.
        Initializes PUP36SIZE to the number of 36-bit words that can be sent
        in one block;
BEGIN "ENETSTART"

INTEGER ErrorCode,
        ConStatus;
STRING ERRMESS;
BOOLEAN FOUND;
INTEGER ARRAY HOSTSOCKET[0:5];

    PupChan ← 6;	! This must be changed *****;
    Connected ← False;
    Print("Looking up grinnell on whitney: ",crlf);
    NAMELOOKUP("whitney+grinnell-server",HOSTSOCKET,FOUND,ERRMESS);
    If NOT FOUND then
	    Print("Error connecting to whitney: ",ERRMESS,crlf)
    Else
	BEGIN
	    Print("Initing connection",crlf);
	    ErrorCode ← PupInit(PupChan,HOSTSOCKET[1],HOSTSOCKET[5],BUFSIZE);
	    If ErrorCode ≠ 0 Then 
		PupError(ErrorCode,
		"Error Initializing Connection - No graphics will be done")
	    Else
		Begin  ! Get handshake;
		    ErrorCode ← PupIn(ConStatus);
		    If (ErrorCode = 0) and (ConStatus = 0) Then 
			Begin
			    Connected ← True;
			    print("Successful open. buffer size: ",bufsize,crlf);
			End
		    Else If (ErrorCode ≠ 0) Then Begin
			PupError(ErrorCode,
			"Could not input handshake - No graphics will be done");
			PupFini;
		      End
		    Else If (ConStatus ≠ 0) Then Begin
			Print("Could not open Grinnel on Vax",crlf);
			Pupfini;
		      End;
		    
		End;
	END;


    PUP36SIZE ← BUFSIZE DIV 2; 	! Buffer Len in 36 bit words;
END "ENETSTART";

INTERNAL PROCEDURE BUFOUT;
Comment BUFOUT outputs any pending Grinnell commands from the 10 memory to the
	Grinnell.;

BEGIN "BUFOUT"
INTEGER BUFLEN,SLEEPY;		Comment number of words to output, No. of sleep cycles;

   BUFLEN ← NXTLOC - 1;  Comment because NXTLOC points to the NEXT location.;
   IF BUFLEN > 0 THEN 
      BEGIN
	IF ONVAX THEN
	    PUPFLUSH
	ELSE
	    GRNBUF(INSBUF,BUFLEN,0);
	NXTLOC ← 1		     comment restart word count;
      END
END "BUFOUT";

PROCEDURE ENETOUT(INTEGER ARRAY INSTRUCTIONS; INTEGER INSTLEFT);
BEGIN "ENETOUT"
Comment This routine outputs an instruction buffer to the currently opened
        Ethernet connection.;
INTEGER LASTINST, 		! Array location of last instruction;
	ERR, 			! Error code retruned from pup routines;
	LENPACKET,		! Length of packet to be sent;
	ENDPACKET,		! Array loc of last instruction for current packet;
	INSTCOUNTER;

    IF CONNECTED THEN BEGIN
 	INSTCOUNTER ← ARRINFO(INSTRUCTIONS, 1);  ! Gets lower bound of array;
	LASTINST ← INSTLEFT - INSTCOUNTER;	 
	BUFOUT;
	! Output instuction buffer until no more instructions are left;
        ERR ← 0;
	WHILE (INSTLEFT > 0) AND (ERR = 0) DO BEGIN
	    PUPOUT(GRNWRITE);  ! Send write indicator;
	    LENPACKET ← (BUFSIZE - 1) MIN INSTLEFT;
            INSTLEFT ← INSTLEFT - LENPACKET;
	    ENDPACKET ← INSTCOUNTER + LENPACKET - 1;
	    WHILE (INSTCOUNTER ≤ ENDPACKET) AND (ERR = 0) DO BEGIN
	        ERR ← PUPOUT(INSTRUCTIONS[INSTCOUNTER]);
		INSTCOUNTER ← INSTCOUNTER + 1;
   	    END;
	    IF ERR = 0 THEN ERR ← PUPFLUSH;
	END;
	PUPERROR(ERR, "Error while outputting instruction buffer");
    END;
END "ENETOUT";
INTERNAL INTEGER PROCEDURE ENETWORDIN;
BEGIN "ENETWORDIN"
Comment This routine inputs data from the grinnell over the ethernet connection.;

INTEGER Word;

    IF CONNECTED THEN BEGIN
        BUFOUT;
	PUPERROR(PUPIN(WORD),"Error while inputing grinnell data");
	RETURN(WORD);
    END;
END "ENETWORDIN";
INTERNAL INTEGER PROCEDURE GWORDIN;
  BEGIN "GWORDIN"
  Comment This procedure causes the 11 to read a word from the 
	  Grinnell interface and then returns this value. It should be uesd after
	  a read back instruction is given;
IF NOT BUSTED THEN
 BEGIN
    IF ONVAX THEN RETURN(ENETWORDIN)
    ELSE BEGIN "ELF INPUT"
	BUFOUT;             
	Comment wait until the DR11B is ready and the arm is not moving;
	WAITFOR11;
	ELFOUT(DRWC, -1);                             ! Readback count 16 bit wds;
	ELFOUT(DRBA,(START11MEM LAND '177776) LSH 1); ! MAKE INTO BYTE ADR;
	ELFOUT(DRST, DRREAD);
	ELFOUT(DRST, GO LOR DRREAD LOR XBA);
	WAITFOR11;
	RETURN(ELFIN(START11MEM))
    END "ELF INPUT"
 END
END "GWORDIN"; 		  Comment input data from 11;

INTERNAL PROCEDURE GRNINI;
Comment GRNINI sets up the Grinnell buffer(set of instructions in PDP11 memory).
	It should be called before GRNINS is ever called.;

BEGIN "GRNINI"
   REQUIRE "APPEND[TST,AAM]" LOAD_MODULE;
   EXTERNAL INTEGER PROCEDURE APPEND(INTEGER CHAN; REFERENCE INTEGER COUNT);
   INTEGER COUNT,BRCHAR,EOF,FAILURE,BUSTCHAN,ACCTCHAN,SAVEW,SAVED;
   STRING MINUTES;
   BOOLEAN NEWFILE;
   INTEGER DATE,DAY,MONTH,TICKS,MIN,HR;

   ONVAX ← FALSE;
  IFC VAXCHK THENC
   PUPCHAN←GETCHAN;
   OPEN(PUPCHAN,"DSK",0,0,0,1,BRCHAR,EOF);
  Comment If ONVAX.GRN[GRI,CR] exists then the Grinnell is assumed to be on the VAX;
   LOOKUP(PUPCHAN,"ONVAX.GRN[GRI,CR]",FAILURE);
   ONVAX ← FAILURE = 0;
   RELEASE(PUPCHAN);
   if onvax then print("Grinnell instructions will be sent to the vax",crlf);
  ENDC
	
   IF ONVAX THEN ENETSTART
   ELSE START11;

   DEMO ← FALSE; COMMENT No demos unless specifically requested;
   NXTLOC ← 1;	Comment set up place to put instructions in buffer;
   EOF ← -1;
IFC GRNDEV THENC
   DO OPEN(LOKCHAN ← GETCHAN,"GRN", 0, 0,0,1,BRCHAR,EOF) UNTIL EOF = 0;
  ELSEC
   OPEN(LOKCHAN ← GETCHAN,"DSK", '17, 0,0,1,BRCHAR,EOF);
   ENTER(LOKCHAN,"GRNLOK.TBL[TMP,AAM]",FAILURE);
   IF FAILURE LAND 3 THEN BEGIN		 comment if file is being used then wait;
	   PRINT("Waiting for Grinnell....");
	   WHILE FAILURE LAND 3 DO BEGIN
		SNOOZE;
		ENTER(LOKCHAN,"GRNLOK.TBL[TMP,AAM]",FAILURE);
           END;
	   PRINT("Gotcha!"&'15&'12);
   END;
  ENDC
  IFC BUSTCHK THENC
   BUSTCHAN←GETCHAN;
   OPEN(BUSTCHAN,"DSK",0,0,0,1,BRCHAR,EOF);
   LOOKUP(BUSTCHAN,"BUSTED.GRN[TMP,AAM]",FAILURE);
   BUSTED ← FAILURE = 0;
   RELEASE(BUSTCHAN);
   IF BUSTED THEN PRINT("Grinnell is busted.  No graphics will be done.",CRLF);
  ENDC
   ACCTCHAN←GETCHAN;
   OPEN(ACCTCHAN,"DSK",0,1,1,COUNT,BRCHAR,EOF);
   LOOKUP(ACCTCHAN,"GRINEL.SUX[DOC,HE]",NEWFILE);
   ENTER(ACCTCHAN,"GRINEL.SUX[DOC,HE]",EOF);
   IF NOT NEWFILE THEN APPEND(ACCTCHAN,COUNT);
   COMMENT Fix the spazz where the caller has done a SETFORMAT;
   GETFORMAT(SAVEW,SAVED); SETFORMAT(0,0);
   OUT(ACCTCHAN,CVXSTR(CALL(0,"GETPPN"))&'11); comment PPN;
   OUT(ACCTCHAN,CVXSTR(CALL(0,"GETNAM"))&'11); comment jobname;
   DATE ← CALL(0,"DATE");   DAY←(DATE MOD 31)+1;  MONTH←((DATE DIV 31) MOD 12)+1;
   OUT(ACCTCHAN,CVS(MONTH)&"/"&CVS(DAY)&'11);
   TICKS ← CALL(0,"TIMER");  MIN←(TICKS DIV 3600)MOD 60; HR←TICKS DIV 216000;
   COMMENT Make life easier for accounting programs;
   MINUTES←CVS(MIN); IF LENGTH(MINUTES)<2 THEN MINUTES←"0"&MINUTES;
   OUT(ACCTCHAN,CVS(HR)&":"&MINUTES&'15&'12);
   SETFORMAT(SAVEW,SAVED);
   RELEASE(ACCTCHAN);
END "GRNINI";

INTERNAL SIMPLE PROCEDURE DEMOMODE(STRING DEVICE,FILENAME);
COMMENT Set up demonstration mode.  Call after calling GRNINI;

BEGIN "DEMOMODE"

DEMO ← TRUE;
EVENWORD ← FALSE;
DEMOCHAN ← GETCHAN;
OPEN(DEMOCHAN,DEVICE,'10,0,19,1,DEMBREAK,DEMEOF);
ENTER(DEMOCHAN,FILENAME,DEMEOF);

END "DEMOMODE";

INTERNAL PROCEDURE DEMOFIN;
COMMENT	Finish off the demo file, if one exists.;

BEGIN "DEMOFIN"
   IF DEMO THEN
	BEGIN
	    COMMENT If not an even number of words, stick in a no-op at the end;
	    IF EVENWORD THEN WORDOUT(DEMOCHAN,(DEMOSAVE LSH 18) LOR NOP);
	    RELEASE(DEMOCHAN);
	    DEMO ← FALSE;
	END;
END "DEMOFIN";

INTERNAL PROCEDURE GRNFIN;
Comment GRNFIN flushes the current Grinnell buffer and releases the ELF.;

BEGIN "GRNFIN"
    BUFOUT;
    DEMOFIN;
    IF ONVAX AND CONNECTED THEN
	BEGIN
	    PUPERROR(PupFini,"Error while closing connection.");
	    RELEASE(PupChan);
	END
    ELSE
       ELFREL;
   RELEASE(LOKCHAN);    Comment release file which locks grinnell;
END "GRNFIN";

PROCEDURE DEMOWORD(INTEGER WORD);
BEGIN
IF EVENWORD THEN
    WORDOUT(DEMOCHAN,(DEMOSAVE LSH 18) LOR WORD)
ELSE
    DEMOSAVE ← WORD;
EVENWORD ← NOT EVENWORD
END;

INTERNAL SIMPLE PROCEDURE GRNINS(INTEGER WORD);
Comment GRNINS puts our one Grinnell instruction.;

BEGIN "GRNINS"
    COMMENT If we are tracing, print the instruction out.;
    IFC SEEINSTR THENC
	PRINT(CVOS(WORD),CRLF);
    ENDC
    COMMENT If we are writing a demo file, write the instruction out.;
    IF DEMO THEN DEMOWORD(WORD);
    COMMENT If we are going to the ethernet, go through the right contortions.;
    IF ONVAX THEN
	BEGIN
	    COMMENT If this is the first word in the buffer, write out the "write"
		code.;
	    IF NXTLOC=1 THEN
		BEGIN
		    PUPOUT(1);
		    NXTLOC ← NXTLOC + 1;
		END;
	    PUPOUT(WORD);
	END
    ELSE
	INSBUF[NXTLOC] ← WORD;
    NXTLOC ← NXTLOC + 1;
    IF NXTLOC > BUFSIZE THEN BUFOUT
END "GRNINS";

INTERNAL PROCEDURE ENETIN(INTEGER ARRAY_ADDR, WORDCOUNT, READBACKMODE(0));
BEGIN "ENETIN"
Comment This routine inputs data from the grinnell over the ethernet connection.;

INTEGER WdCounter, Word;

    IF CONNECTED THEN BEGIN
	GRNINS(RPD LOR READBACKMODE);
	BUFOUT;             
	pupout(2); pupout(wordcount); pupflush;
	FOR WdCounter ← 0 step 1 until WordCount - 1 DO BEGIN
	    PUPERROR(PUPIN(WORD),"Error while inputing grinnell data");
	    MEMORY[ARRAY_ADDR + WDcounter,INTEGER] ← WORD;
	END;
    END;
END "ENETIN";

INTERNAL PROCEDURE GRNIN(INTEGER ADDR, COUNT, READBACKMODE(0), USETBITS(0));
  BEGIN "GRNIN"
  INTEGER SLEEPY,CT;
  IFC HANGRETRY THENC
  BOOLEAN HUNG; COMMENT Tells whether the DR11 is well hung;
  ENDC
  Comment This procedure causes the 11 to read count words from the 
	  Grinnell interface and then returns this value. It should be used after
	  a SPD instruction with at least the readback bit set;

  IF ONVAX THEN ENETIN(ADDR, COUNT, READBACKMODE)
  ELSE BEGIN "ELF INPUT"

    IFC HANGRETRY THENC
    DO BEGIN "TRY TO READ BUFFER"
    HUNG ← FALSE;
    ENDC
    GRNINS(RPD LOR READBACKMODE);
    BUFOUT;		
 IF NOT BUSTED THEN
  BEGIN "READ IN A BLOCK FROM GRINNELL"
    WAITFOR11;
    ELFOUT(DRWC, -COUNT);                         Comment Readback count 16 bit wds;
    ELFOUT(DRBA,(START11MEM LAND '177777) LSH 1); comment MAKE INTO BYTE ADR;
ifc trace thenc
print("type <cr> to initiate read (set GO bit):");inchwl;
endc
    ELFOUT(DRST, DRREAD);
    ELFOUT(DRST, GO LOR DRREAD LOR XBA);

Comment Now we need to wait for the DR-11 to get finished.  We used to call SNOOZE
	but it always waits 1/60th sec min, which was too slow.  We have experi-
	mentally determined that COUNT*4 is not enough and COUNT*5 is the minimum
	that works.;
    CT ← COUNT*5;
    QUICK_CODE
	LABEL LOOP;
	MOVE  '13,CT;
LOOP:	SOJG  '13,LOOP;
    END;

ifc trace thenc print("x"); endc
	SLEEPY ← 0;
	WHILE ¬(ELFIN(DRST) LAND READY) DO 
	    BEGIN SNOOZE; SLEEPY←SLEEPY+1;
IFC TRACE THENC
!		  IF SLEEPY > 20 THEN ;
		    PRINT("DRST: ",CVOS(ELFIN(DRST)),'15&'12);
ENDC
comment Code has been added to detect a hung DR11B...AAM 11/4/80;
		IF SLEEPY > 30  COMMENT If DR11 doesn't respond in .5 sec;
		THEN BEGIN "HUNG DR11"
		INTEGER I,WC;
		IFC HANGNOISY THENC
	       Comment extend the sign of a 16-bit word (bug fixed 12/19/80 AAM);
		WC←ELFIN(DRWC) LOR '777777600000;
		PRINT("DR11 status register (octal) = ",CVOS(ELFIN(DRST)),CRLF);
		PRINT(CRLF,"The DR-11B hung thinking it had to read ",
		 -WC," (decimal) more words.  The words it read are:",
		 " (octal)",CRLF);
		ELFBKI(START11MEM,COUNT+WC,ADDR,0);
		FOR I←0 STEP 1 UNTIL COUNT+WC-1 DO
		    PRINT(CVOS(MEMORY[ADDR+I]),CRLF);
		ENDC
		IFC HANGWAIT THENC
		PRINT("Type <cr> to do a power fail reset: ");INCHWL;
		ENDC
		IFC HANGRETRY THENC
		HUNG ← TRUE;
		ENDC
		POWERFAIL;
		END "HUNG DR11"
	    END;
    IFC SPEEDCHECK THENC PRINT("I slept ",SLEEPY," times.",CRLF); ENDC
    IFC HANGRETRY THENC
    END "TRY TO READ BUFFER" UNTIL NOT HUNG;
    ENDC
    Comment check to see if words are packed when reading from the elf;
    IF (USETBITS LAND TWOWRDSL) THEN COUNT ← COUNT/2 + (COUNT MOD 2);
    ELFBKI(START11MEM,COUNT, ADDR,USETBITS);	  Comment input data from 11;
  END "READ IN A BLOCK FROM GRINNELL";
  END "ELF INPUT";
  END "GRNIN";        
INTERNAL SIMPLE PROCEDURE EXE11BUF(INTEGER ADDR, BUFLEN);
  Comment this procedure is for fast execution of grinnell commands that are 
  	  repeated often.  It starts the DR11B using the ADDR and WORDCOUNT;
  BEGIN "EXE11BUF"
	IF NOT BUSTED THEN BEGIN
	    WHILE ¬(ELFIN(DRST) LAND READY) DO ;
	    ELFOUT(DRWC,-BUFLEN);   Comment NOTE: word count not byte count;
	    ELFOUT(DRBA,(ADDR LAND '177777) LSH 1); comment MAKE INTO BYTE ADR;
	    ELFOUT(DRST,DRWRITE);        comment Grinnell needs at least one clock;
					 comment cycle between setting of;
	    ELFOUT(DRST,GO LOR DRWRITE LOR ((ADDR LAND '300000) LSH -11));     
				 comment write and go with extended bus addr bits;
	END;
   
  END "EXE11BUF";

INTERNAL SIMPLE PROCEDURE QUICKBUF;
  Comment this procedure is for fast execution of grinnell commands that are 
  	  repeated often.  It starts the DR11B using the ADDR and WORDCOUNT;
  BEGIN "QUICKBUF"
   OWN INTEGER BUFLEN;
   BUFLEN ← NXTLOC - 1;
   IF BUFLEN > 0 THEN 
      BEGIN
	NXTLOC ← 1;		     comment restart word count;
	IF NOT BUSTED THEN BEGIN
	    WHILE ¬(ELFIN(DRST) LAND READY) DO ;
	    ELFBKO(START11MEM,(BUFLEN),INSBUF,0); Comment move the buffer to the 11;
	    ELFOUT(DRWC,-BUFLEN);   Comment NOTE: word count not byte count;
	    ELFOUT(DRBA,(START11MEM LAND '177777) LSH 1); comment MAKE INTO BYTE ADR;
	    ELFOUT(DRST,DRWRITE);        comment Grinnell needs at least one clock;
					 comment cycle between setting of;
	    ELFOUT(DRST,GO LOR DRWRITE LOR ((START11MEM LAND '300000) LSH -11));  
				 comment write and go with extended bus addr bits;
	END;
   
    END;
  END "QUICKBUF";

INTERNAL SIMPLE PROCEDURE QUICKGIN(INTEGER ADDR, COUNT, READBACKMODE(0), USETBITS(0));
  Comment this procedure is for fast input of data from the Grinnell to the  
          11.  It starts the DR11B using the ADDR and WORDCOUNT
	  Since it does not wait (i.e. no snooze) it is  suitable for space war 
	  mode;
 Comment An RPD instruction must be given prior to the call of this procedure;
  BEGIN "QUICKGIN"
    INTEGER CT;
    IF NOT BUSTED THEN BEGIN
	GRNINS(RPD LOR READBACKMODE);
!	PRINT("waiting before loop 1 ", COUNT, crlf);
	QUICKBUF;
	for ct ← 1 step 1 until COUNT*5 do;
!	    CT ← COUNT*5;
!	    QUICK_CODE
!		LABEL LOOP1;
!		MOVE  '13,CT;
!	LOOP1:  SOJG  '13,LOOP1;
!	    END;
!	PRINT("waiting at loop 1",crlf);
	WHILE ¬(ELFIN(DRST) LAND READY) DO ;  Comment Yes, this is the wait loop;
	ELFOUT(DRWC,-COUNT);	Comment NOTE: word count not byte count;
	ELFOUT(DRBA,(START11MEM LAND '177777) LSH 1); comment MAKE INTO BYTE ADR;
	ELFOUT(DRST,DRREAD);	     comment Grinnell needs at least one clock;
				     comment cycle between setting of;
	ELFOUT(DRST,GO LOR DRREAD LOR ((START11MEM LAND '300000) LSH -11));     
				 comment write and go with extended bus addr bits;
!	CT ← COUNT*5;
!	QUICK_CODE
!	    LABEL LOOP2;
!	    MOVE  '13,CT;
!   LOOP2:  SOJG  '13,LOOP2;
!	END;
!	PRINT("waiting before loop 2 ", COUNT, crlf);
	for ct ← 1 step 1 until COUNT*5 do;
!	PRINT("waiting at loop 2",crlf);
	WHILE ¬(ELFIN(DRST) LAND READY) DO ;  comment don't leave until done;
	Comment check to see if words are packed when reading from the elf;
	IF (USETBITS LAND TWOWRDSL) THEN COUNT ← COUNT/2 + (COUNT MOD 2);
	ELFBKI(START11MEM,COUNT, ADDR,USETBITS);      Comment input data from 11;
    END
   
  END "QUICKGIN";
INTERNAL PROCEDURE TSTGRN(INTEGER TESTNO);
Comment GRNTST runs the internal diagnostic test on the Grinnell.  These are
	designed to be run with TESTNO = 0, then 1, then 2 then 3, but this is
	not mandatory.;
BEGIN "GRNTST"
   GRNINS(SPD LOR TEST);
   GRNINS(LPA LOR (TESTNO MOD 4));
   BUFOUT;
END "GRNTST";
INTERNAL PROCEDURE MAPGRN(INTEGER CARD,TABL,NBITS);
Comment MAPGRN sets the video lookup table at CARD, TABL to show the least
	significant NBITS bits.;
BEGIN "MAPGRN"
    INTEGER ARRAY TMAP[0:255];
    INTEGER I, BITMASK, LSHIFT;
    BITMASK ← 2↑NBITS - 1; Comment mask with low NBITS on;
    LSHIFT ← 8 - NBITS;    Comment how far to shift the low NBITS;
    FOR I ← 0 STEP 1 UNTIL 255 DO TMAP[I] ← (I LAND BITMASK) LSH LSHIFT;
    IFVCMAP(CARD, TABL, TMAP);
    BUFOUT;
END "MAPGRN";
INTERNAL PROCEDURE ERASEG(INTEGER CHAN,BITS('7777));
Comment Erase the indicated bits in the indicated channel;
BEGIN "ERASEG"
   GRNINS(LDC LOR (1 LSH CHAN));
   GRNINS(LSM LOR BITS);
   GRNINS(LWM LOR 0); Comment dark background;
   GRNINS(ERS);
   BUFOUT;
END "ERASEG";
INTERNAL PROCEDURE VIDGRN(INTEGER LEFT, TOP, CHAN; INTEGER ARRAY PIC;
				INTEGER SHIFTBY(0));
Comment VIDGRN outputs a gray-scale picture to the Grinnell.
	LEFT - left margin of picture in pixels
	TOP - top margin of picture in pixels
	CHAN - channel of output;
BEGIN "VIDGRN"
INTEGER I;
INTEGER LINE,PIXEL;		Comment Line number being output;
INTEGER UNPACK;			Comment Address of unpacking "subroutine";
INTEGER ARRAY LINBUF[0:PIC[LNBY]]; Comment Buffer sent to 11;

   GRNINS(LDC LOR (1 LSH CHAN));	Comment Select channel;
   GRNINS(LSM LOR '377);	Comment enable all subchannels;
   GRNINS(LWM LOR BITZ);	Comment Set write mode;
ifc TRACE thenc
print("Here we go on channel ",chan);
endc
   GRNINS(LUM LOR E1);		Comment Ea ← Ea+Eb mode;
   GRNINS(LEB LOR 1);		Comment Eb ← 1;
   GRNINS(LLB LOR NEG1);	Comment Lb ← -1;
   GRNINS(LLA LOR TOP);		Comment La ← top margin;
   GRNINS(LEC LOR LEFT);	Comment Ec ← left margin;
   GRNINS(LEA LOR LEFT);	Comment Set Ea to starting value;
Comment Flush anything that might be left over in the PDP11 buffer;
BUFOUT;

Comment Now that all control registers are set up, output all the lines of data.;

Comment I'm trying a new hack here, so for now I'm going to do things unpacked;

Comment assemble up instructions for unpacking one line into LINBUF;
Comment runtime assembler courtesy of PB;
BEGIN "RUNTIME ASSEMBLY"
REQUIRE "ASSEMB.SAI[S,HE]" SOURCE_FILE;
CBLK$_BEGIN(1);
L$(UNPACK);
FOR PIXEL ← 0 STEP 1 UNTIL PIC[LNBY]-1 DO
    BEGIN "UNPACK AND STORE ONE PIXEL"
C$	ILDB(2,1);
IF SHIFTBY≠0 THEN C$ LSH(2,hr(SHIFTBY)); Comment HHB this crocko'shit doesnt work if
		SHIFTBY<0 ... generates SHIFTBY !!!! changed to HR(Y) 25-5;
C$	MOVEM(2,LOC(LINBUF[0])+PIXEL);
    END "UNPACK AND STORE ONE PIXEL";
C$	POPJ('17,0);
CBLK$_END(1);
END "RUNTIME ASSEMBLY";

Comment put SLU instruction at end of buffer;
LINBUF[PIC[LNBY]]←(SLU LOR L1 LOR E0); Comment Return to left margin and next line
				(this is La ← La+Lb  and  Ea ← Ec);

IF PIC[LNBY]+1 > LEN11MEM THEN PRINT("Oh no!!!!!!!  HELP!!!!!!!");

Comment Put the ELF in "risky but fast" mode (special system hack);
IF NOT ONVAX THEN RISKON;

Comment For each line;
FOR LINE ← 0 STEP 1 UNTIL PIC[PCLN]-1 DO
    BEGIN
	INTEGER BYTPTR;

	Comment Set up byte pointer to the line in AC1, then unpack the
		line into LINBUF;
	BYTPTR←MEMORY[PIC[BPTAB]-1]+PIC[LINTAB+LINE];
	START_CODE
	    MOVE 1,BYTPTR;
	    PUSHJ '17,@UNPACK;
	END;

	Comment Transfer the instructions to the PDP-11...;
	IF ONVAX THEN  ENETOUT(LINBUF,PIC[LNBY]+1)
	ELSE GRNBUF(LINBUF,PIC[LNBY]+1,0);
	IF DEMO THEN
	    FOR I ← 0 STEP 1 UNTIL PIC[LNBY] DO DEMOWORD(LINBUF[I]);
    END;

Comment Take the ELF out of its funny mode;
IF NOT ONVAX THEN RISKOFF;

END "VIDGRN";

! routines to input a picture from the grnnell;
PROCEDURE PACKBYTES(INTEGER BYTE1PTR, BYTE2PTR,DESTPTR, NBYTES);
  BEGIN "PACKBYTES"
     LABEL LOOP;
     START_CODE
	MOVE    3,BYTE1PTR;
	MOVE	4,BYTE2PTR;
	MOVE	5,DESTPTR;
	MOVE 	1,NBYTES;
	ash	1,-1;
  LOOP: LDB	2,3;
	IDPB	2,5;
	AOS	3;
	LDB	2,4;
	IDPB	2,5;
	AOS	4;
	SOJG	1,LOOP;
    END;
 END "PACKBYTES";
	
  
INTERNAL PROCEDURE InLine(REFERENCE INTEGER  Ptr; INTEGER linelen);
  BEGIN "InLine"
   INTEGER element;
   INTEGER ARRAY LineIn[1:Linelen/2];  


 ! Now get one scan line from the Grinnell and store it in the 11`s buffer;
 ! and input line from the 11`s memory through the ELF;
 ! The data is read in in packed mode from the Grinnell and then two words per;
 ! word left justified from the 11;
   GRNIN(LOCATION(LINEIN[1]), Linelen/2, BYTEPACK,TWOWRDSL);
 
   FOR element ← 0 STEP 1 UNTIL linelen/4 -1 DO
	MEMORY[ptr + element] ← lineIn[element +1];

 END "InLine";

INTERNAL PROCEDURE INPIC(INTEGER CHAN; INTEGER ARRAY PIC);

 BEGIN "InPic"
   INTEGER line, ptr;
   DEFINE HEIGHT = 512,
	  WIDTH  = 512,
	  BITS   = 8;

 ! Make blank picture with dimensions HxWxB;
   MAKPIX(HEIGHT,WIDTH,BITS,PIC[0]); 


   GRNINI;			! Set up Grinnell buffers;

   GRNINS(LDC LOR (1 LSH CHAN));	! Select channel;
   GRNINS(SPD LOR READBACK);	! Set read mode;
   GRNINS(LUM LOR E1);		! Ea ← Ea+Eb mode;
   GRNINS(LEB LOR 1);		! Eb ← 1;
   GRNINS(LLB LOR NEG1);	! Lb ← -1;
   GRNINS(LLA LOR HEIGHT-1);	! La ← top margin;
   GRNINS(LEC LOR 0);		! Ec ← left margin;
   GRNINS(LEA LOR 0);		! Set Ea to starting value;

 ! Send these commands off to the Grinnell;
   BUFOUT;

 ! Input first line of picture form Grinnell;
   PTR ← MEMORY[PIC[BPTAB]-1,INTEGER] + PIC[LINTAB]; Comment get byte ptr;
   InLine(Ptr, width);

 ! Now get the rest;
   FOR line ← 2 step 1 until Height DO
     BEGIN
       GRNINS(SLU LOR L1 LOR E0);
       BUFOUT;
       PTR ← MEMORY[PIC[BPTAB]-1,INTEGER]+PIC[LINTAB+LINE-1]; Comment get byte ptr;
       IfC trace THENC
         print(line," ");
       ENDC
       InLine(Ptr, width);
     END;

 ! Finished so relaese ELF;
   GRNFIN;

 END "InPic";


INTERNAL PROCEDURE UINPIC(INTEGER CHAN; INTEGER ARRAY PIC);

 BEGIN "UInPic"
   INTEGER line, ptr;
   INTEGER ARRAY LINEIN[1:256];
   DEFINE HEIGHT = 512,
	  WIDTH  = 512,
	  BITS   = 8;

 ! Make blank picture with dimensions HxWxB;
   MAKPIX(HEIGHT,WIDTH,BITS,PIC[0]); 


   GRNINI;			! Set up Grinnell buffers;

   GRNINS(LDC LOR (1 LSH CHAN));	! Select channel;
   GRNINS(LSM LOR '377);	! Enable read back of all 8 bits;
   GRNINS(LEA LOR 0);		! Set Ea to starting value;
   GRNINS(LEB LOR 1);		! Eb ← 1;
   GRNINS(LLA LOR HEIGHT-1);	! La ← top margin;
   GRNINS(LLB LOR '777);	! Lb ← -1;
   GRNINS(LEC LOR 0);		! Ec ← left margin;
   GRNINS(LUM LOR E1);		! Ea ← Ea+Eb mode;
   GRNINS(SPD LOR READBACK);	! Set read mode;

 ! Send these commands off to the Grinnell;
   BUFOUT;

 ! Input first line of picture form Grinnell;
   PTR ← MEMORY[PIC[BPTAB]-1,INTEGER] + PIC[LINTAB]; Comment get byte ptr;
   GRNIN(LOCATION(LINEIN[1]), WIDTH,0,TWOWRDSL);
   PACKBYTES(POINT(BITS,LINEIN[1],15),POINT(BITS,LINEIN[1],31),PTR,WIDTH);

 ! Now get the rest;
   FOR line ← 2 step 1 until Height DO
     BEGIN
       GRNINS(SLU LOR L1 LOR E0);	
       PTR ← MEMORY[PIC[BPTAB]-1,INTEGER]+PIC[LINTAB+LINE-1]; Comment get byte ptr;
       IFC TRACE THENC
	   print(line," ");
       ENDC
       GRNIN(LOCATION(LINEIN[1]), WIDTH,0,TWOWRDSL);
       PACKBYTES(POINT(BITS,LINEIN[1],15),POINT(BITS,LINEIN[1],31),PTR,WIDTH);
	comment shit, changed to 31 after several days of crappy input;
     END;

 ! Finished so release ELF;
   GRNFIN;

 END "UInPic";
END "GRNSAI";